using System;
using System.Numerics;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Combina.Core.Test
{
    /// <summary>
    /// Summary description for CombinatoricsCalculatorTests
    /// </summary>
    [TestClass]
    public class CombinatoricsCalculatorTests
    {
        public CombinatoricsCalculatorTests()
        {
        }

        /// <summary>
        /// Gets or sets the test context which provides
        /// information about and functionality for the current test run.
        /// </summary>
        public TestContext TestContext { get; set; }

        #region Additional test attributes
        //
        // You can use the following additional attributes as you write your tests:
        //
        // Use ClassInitialize to run code before running the first test in the class
        // [ClassInitialize()]
        // public static void MyClassInitialize(TestContext testContext) { }
        //
        // Use ClassCleanup to run code after all tests in a class have run
        // [ClassCleanup()]
        // public static void MyClassCleanup() { }
        //
        // Use TestInitialize to run code before running each test 
        // [TestInitialize()]
        // public void MyTestInitialize() { }
        //
        // Use TestCleanup to run code after each test has run
        // [TestCleanup()]
        // public void MyTestCleanup() { }
        //
        #endregion

        [TestMethod]
        public void Factorial64Test()
        {
            ulong expectedValue = 1;
            for (int n = 0; n <= Factorial.MaxN64; ++n)
            {
                ulong previousValue = expectedValue;
                if (n != 0)
                    expectedValue = expectedValue * (ulong)n;

                ulong actualValue = Factorial.Calculate64(n);
                Assert.AreEqual(expectedValue, actualValue, "i=" + n);
                Assert.IsTrue(previousValue <= expectedValue, string.Format("i = {0}. Previous = {1}. Current = {2}.", n, previousValue, expectedValue));
            }
        }

        [TestMethod]
        public void FactorialTest()
        {
            BigInteger currentValue = 1;
            for (int i = 0; i <= 323; ++i)
            {
                BigInteger previousValue = currentValue;
                if (i != 0)
                    currentValue = currentValue * i;
                Assert.AreEqual(currentValue, Factorial.Calculate(i), "i=" + i);
                Assert.IsTrue(previousValue <= currentValue, string.Format("i={0}. Previous={1}. Current={2}.", i, previousValue, currentValue));
            }
        }

        [TestMethod]
        public void Choose64Test()
        {
            for (int n = 0; n <= Choose.MaxN64; ++n)
            {
                for (int k = 0; k <= n; ++k)
                {
                    BigInteger expectedValue = Factorial.Calculate(n) /
                                               Factorial.Calculate(k) /
                                               Factorial.Calculate(n - k);
                    ulong actualValue = Choose.Calculate64(n, k);
                    Assert.AreEqual(expectedValue, actualValue, string.Format("n={0}. k={1}", n, k));
                }
            }
        }

        [TestMethod]
        public void ChooseTest()
        {
            double sumPercent = 0;
            int count = 0;

            for (int n = 0; n <= 320; ++n)
            {
                for (int k = 0; k <= n; ++k)
                {
                    DateTime start = DateTime.Now;
                    BigInteger expectedValue = Factorial.Calculate(n) /
                                               Factorial.Calculate(k) /
                                               Factorial.Calculate(n - k);
                    DateTime end = DateTime.Now;
                    TimeSpan diff1 = end - start;

                    start = DateTime.Now;
                    BigInteger actualValue = Choose.Calculate(n, k);
                    end = DateTime.Now;
                    TimeSpan diff2 = end - start;

                    if (diff1 != diff2)
                    {
                        if (diff1 > diff2)
                            sumPercent += 1 - (diff2.TotalMilliseconds / diff1.TotalMilliseconds);
                        else
                            sumPercent -= 1 - (diff1.TotalMilliseconds / diff2.TotalMilliseconds);
                    }
                    ++count;

                    Assert.AreEqual(expectedValue, actualValue, string.Format("n={0}. k={1}", n, k));
                }
            }

            double averagePercent = sumPercent / count;
            Assert.IsTrue(averagePercent > 0);
        }

        [TestMethod]
        public void ChooseMaxTest()
        {
            for (int n = 0; n <= 2244; ++n)
            {
                for (int k = 0; k <= n; ++k)
                {
                    BigInteger actualValue = Choose.Calculate(n, k);
                    Assert.IsTrue(actualValue > 0);
                }
            }
        }
    }
}
